#include "bc.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define PRINT_HEADER	0x01
#define PRINT_POOL		0x02
#define PRINT_CS_HEADER	0x04
#define PRINT_CS		0x08
#define PRINT_INFOS		0x10

#define DEFAULT_ACTION	PRINT_CS

#define error(code, ...) \
	do {\
		fprintf(stderr, __VA_ARGS__);\
		exit(code);\
	}while(0)

#define warning(...) \
	do {\
		fprintf(stderr, __VA_ARGS__);\
	}while(0)

const char* syntax = "Syntax: rbcp [-h|--header|-p|--pool|-c|--code|-H|--cs-header|-s|--summary|-a|--assembly|-b|--binary|-i|--infos|-?|--help]* [-C|--only-code <codeseg>] [--] filename\n";

static int argc;
static char **argv;
static int verbose = 0;
static int show_assembly = 1;

static bc_header header;
static cpool_entry *cpool;
static fun_info *infos;
static code_segment segments;

int parseOptions(char **fname, int default_action){
	int dashdash = 0;
	int action = 0;
	for(int i = 1; i < argc; i++){
		char *current = argv[i];
		if(current[0] == '-' && !dashdash){
			if(!strcmp(current, "-v") || !strcmp(current, "--verbose"))
			  verbose ++;
			else if(!strcmp(current, "-h") || !strcmp(current, "--header"))
			  action |= PRINT_HEADER; 	
			else if(!strcmp(current, "-p") || !strcmp(current, "--pool"))
			  action |= PRINT_POOL; 	
			else if(!strcmp(current, "-c") || !strcmp(current, "--code"))
			  action |= PRINT_CS; 	
			else if(!strcmp(current, "-H") || !strcmp(current, "--cs-header"))
			  action |= PRINT_CS_HEADER;
			else if(!strcmp(current, "-s") || !strcmp(current, "--summary"))
			  action |= PRINT_CS_HEADER | PRINT_HEADER;
			else if(!strcmp(current, "-i") || !strcmp(current, "--infos"))
			  action |= PRINT_INFOS;
			else if(!strcmp(current, "-a") || !strcmp(current, "--assembly"))
			  show_assembly = 1;
			else if(!strcmp(current, "-b") || !strcmp(current, "--binary"))
			  show_assembly = 0;
			else if(!strcmp(current, "-?") || !strcmp(current, "--help"))
			  error(0, "%s", syntax);
			else if(!strcmp(current, "--"))
			  dashdash ++;
			else 
				warning("Unknown option: %s\n", current);
		} else 
			if(*fname) {
			  warning("More than one filename given: '%s', '%s'\n", *fname, current);
			} else { // Not efficient, but we don't mind
			  int len = strlen(current);
			  if(!strcmp(current + len - 2, ".R")){
				current = strcpy(malloc(len + 2), current);
				current[len] = 'c';
				current[len + 1] = '0';
			  }
			  *fname = current;
			}
	}
	return (!action) ?  default_action : action;
}

#define PRINTF (void (*)(const char *, ...))printf
int main(int _argc, char **_argv){
	argc = _argc;
	argv = _argv;
	char *fname = NULL;
	FILE *fd;
	int action = parseOptions(&fname, DEFAULT_ACTION);
	if(!fname)
		error(1, "No filename given !\n%s", syntax);
	if(!(fd = fopen(fname, "rb")))
		error(2, "Can't open '%s': %s\n", fname, strerror(errno));
	if(read_bc_file(fd, &header, &cpool, &infos, &segments, malloc) < 0)
		error(3, "Bad Magic/not a R ByteCode file! (0x%x != 0x%x)\n", *(int*)BC_MAGIC, *(int*)header.magic);
	do{
		switch(action & -action){
			case PRINT_HEADER:
				dumpBCHeader(&header, PRINTF);
				break;
			case PRINT_POOL:
				dumpPool(header.pp, cpool, PRINTF);
				break;
			case PRINT_CS_HEADER:
				dumpSegmentHeader(header.cs, segments, PRINTF);
				break;
			case PRINT_INFOS:
				for(int i = 0; i < header.funinfos; i++)
					printf("[%x] %.4x => %.4x params@%x\n", i, infos[i].start, infos[i].end, infos[i].params);
				break;
			case PRINT_CS:
				if(show_assembly)
					disassemble(header.cs, segments, cpool, PRINTF);
				else
					dumpBC(header.cs, segments, PRINTF);
				break;
		}
	} while(action ^= action & -action);
	fclose(fd);
	return 0;
}
